<?php

namespace app\backend\services\auth;

/**
 * 权限认证类
 *
 * Class Auth
 *
 * @package auth
 * Author: zsw iszsw@qq.com
 */
class Authority
{

    /**
     * 所有权限 树状
     *
     * @var array<Permission>
     */
    protected $permissions = [];

    /**
     * 权限索引 [route => Permission]
     *
     * @var array<Permission>
     */
    protected $permissionMaps = [];

    /**
     * 已授权权限
     *
     * @var null|array<Permission>
     */
    protected $authorizedPermissionMaps = [];

    /**
     * 超级管理员
     * 不校验权限
     *
     * @var bool
     */
    protected $administrator = false;

    /**
     * @var UserInterface
     */
    protected $user;

    /**
     * @var array<RoleInterface>
     */
    protected $roles = [];

    /**
     * @var string
     */
    protected $error = '';

    /**
     * 允许访问未加入的权限
     *
     * @var bool
     */
    protected $allowHomeless = true;

    /**
     * 延迟注册权限
     *
     * 解决parent比自己后注册
     *
     * @var array
     */
    protected $delayRegisterPermissions = [];

    /**
     * 加入权限
     *
     * @param array           $permissions
     * @param Permission|null $parent
     * @param bool            $main 注册到根权限下
     *
     * @return array
     */
    public function addPermissions(array $permissions, ?Permission $parent = null, $main = true): array
    {
        $data = [];
        $permissions = $this->mergePermissions($permissions);
        foreach ($permissions as $permission)
        {
            if ( ! $permission instanceof Permission)
            {
                $permission = new Permission($permission);
                if (is_array($permission->children))
                {
                    $permission->addChildren($this->addPermissions($permission->children, $permission, false));
                }
            }

            if (null === $parent)
            {
                if ($permission->parent)
                {
                    array_push(
                        $this->delayRegisterPermissions, [
                                                           'parent'  => $permission->parent,
                                                           'current' => $permission,
                                                           'main'    => $main,
                                                       ]
                    );
                } else
                {
                    // 不存在父级才能加入
                    $main && array_push($this->permissions, $permission);
                }
            } else
            {
                $permission->setParent($parent);
            }

            array_push($data, $permission);
            $this->addPermissionMap($permission);
        }

        if (null === $parent)
        {
            $this->delayRegisterPermission();
        }

        return $data;
    }


    /**
     * 合并多维数组
     *
     * @param array $data
     * @param array $list
     *
     * @return array
     */
    private function mergePermissions(array $data, &$list = [])
    {
        foreach ($data as $k => $b) {
            if (is_numeric($k) && is_array($b)) {
                $this->mergePermissions($b, $list);
            }else{
                $list[] = $data;
                break;
            }
        }
        return $list;
    }

    /**
     * 延迟处理 parent 数据
     */
    private function delayRegisterPermission(): void
    {
        if (count($this->delayRegisterPermissions) > 0)
        {
            foreach ($this->delayRegisterPermissions as $p)
            {
                $parent = $p['parent'];
                /* @var Permission $current */
                $current = $p['current'];
                $main = $p['main'] ?? false;

                if (isset($this->permissionMaps[$parent]))
                {
                    $current->setParent($this->permissionMaps[$parent]);
                    $this->permissionMaps[$parent]->addChildren($current);
                } else if ($main)
                {
                    array_push($this->permissions, $current);
                }
            }

        }
    }

    /**
     * 索引路由
     *
     * @param Permission $permission
     */
    public function addPermissionMap(Permission $permission)
    {
        $this->permissionMaps[$permission->route] = $permission;
    }


    /**
     * 判断当前用户权限
     * 如果出现多个相同路由 满足一个就通过
     *
     * @param string | array $route
     *
     * @return bool
     */
    public function can($route): bool
    {
        $permission = $this->getPermissionMap(is_array($route) ? $route['route'] : $route);

        if (is_null($permission))
        {
            return $this->allowHomeless;
        }

        return $permission->check($route, $this);
    }

    /**
     * 获取所有规则树状
     *
     * @return array
     */
    public function getPermissions(): array
    {
        return $this->permissions;
    }

    /**
     * 规则列表
     *
     * @param string $route
     *
     * @return Permission | array<Permission>
     */
    public function getPermissionMap($route = null)
    {
        return is_null($route) ? $this->permissionMaps : $this->permissionMaps[$route] ?? null;
    }

    /**
     * 是否授权
     *
     * @param Permission $permission
     *
     * @return bool
     */
    public function isAuthorized(Permission $permission): bool
    {
        return $this->isAdministrator() ?: isset($this->authorizedPermissionMaps()[$permission->route]);
    }

    /**
     * 分派角色
     *
     * @param array<RoleInterface> $roles
     *
     * @return $this
     */
    public function assignRole($roles): self
    {
        if (is_array($roles))
        {
            foreach ($roles as $role)
            {
                $this->assignRole($role);
            }
        } else
        {
            if ($roles instanceof RoleInterface)
            {
                $this->roles[] = $roles;
            }
        }

        $this->authorizedPermissionMaps = [];

        return $this;
    }

    protected function authorizedPermissionMaps($route = null)
    {
        empty($this->authorizedPermissionMaps) && $this->authorize();

        return is_null($route) ? $this->authorizedPermissionMaps : $this->authorizedPermissionMaps[$route] ?? null;
    }

    /**
     * 授权角色权限
     *
     * @return array
     */
    protected function authorize()
    {
        $roles = $this->roles();

        $permissions = [];
        /** @var RoleInterface $role */
        foreach ($roles as $role)
        {
            $routes = $role->permissions();
            foreach ($routes as $route)
            {
                if ($permission = $this->getPermissionMap($route)) {
                    $this->addAuthorizePermissionMap($permission);
                }
            }
        }

        return $permissions;
    }

    /**
     * 添加授权索引
     *
     * @param Permission $permission
     */
    private function addAuthorizePermissionMap(Permission $permission)
    {
        $this->authorizedPermissionMaps[$permission->route] = $permission;
    }

    /**
     * 获取角色
     *
     * @return array<RoleInterface>
     */
    public function roles(): array
    {
        return $this->roles;
    }

    /**
     * 错误
     *
     * @return string
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * 设置当前超级管理员
     *
     * @param bool $value
     *
     * @return $this
     */
    public function administrator(bool $value = true): self
    {
        $this->administrator = $value;

        return $this;
    }

    /**
     * @return bool
     */
    public function isAdministrator(): bool
    {
        return $this->administrator;
    }

    /**
     * 用户
     *
     * @return UserInterface
     */
    public function user()
    {
        return $this->user;
    }

    /**
     * 设置用户
     *
     * @param UserInterface $user
     *
     * @return $this
     */
    public function setUser(UserInterface $user): self
    {
        $this->user = $user;

        return $this;
    }

    /**
     * 游客
     *
     * @return bool
     */
    public function guest(): bool
    {
        return ! $this->user;
    }

}
